/*
 * Copyright © 2010 LPRM Universidade Federal do Espírito Santo - RNP-GT CWTools
 * 
 * This file is part of GW-Android.
 * 
 * GW-Android is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * GW-Android is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with GW-Android. If not, see <http://www.gnu.org/licenses/>.
 */
package br.ufes.cwtools.gw.android.components.image_upload;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Address;
import android.location.Geocoder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import br.ufes.cwtools.gw.android.components.Component;
import br.ufes.cwtools.gw.android.connection_service.ConnectionServiceConnection;
import br.ufes.cwtools.gw.android.connection_service.IConnectionService;
import br.ufes.cwtools.gw.android.location_service.ILocationService;

public class ImageUploader extends Component {
    private ServiceConnection connection;

    private static final String LOG_TAG = "ImageUploader";
    private static final String IMAGE_UPLOAD_SUFIX = "/registra?_format=xml";
    // Classe da entidade do GW com a qual o componente interage
    private static final String ENTITY_CLASS = "br.org.groupwareworkbench.arquigrafia.photo.Photo";

    private String mFormAction;
    private boolean cancela;
    private String imageUri;

    double mLatitude, mLongitude;
    boolean mGotGPSData = false;

    ProgressDialog pd;

    ServiceConnection mLocationConnection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            ILocationService ils = ILocationService.Stub.asInterface(service);

            Log.v(LOG_TAG, "Conectei ao LocationService!");
            try {
                if (ils.hasFix()) {
                    Log.v(LOG_TAG, "Posição válida! Estou enviando-a!");
                    mGotGPSData = true;
                    mLatitude = ils.getLatitude();
                    mLongitude = ils.getLongitude();
                } else {
                    Log.v(LOG_TAG, "Posição inválida!");
                }
            } catch (Exception e) {
                Log.e(LOG_TAG, "Erro na aquisição dos dados do GPS.", e);
                mGotGPSData = false;
            }

            unbindService(this);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.component_image_uploader);

        mFormAction = getConfiguration(EXTRA_APPLICATION_URL)
                + getConfiguration(EXTRA_BASE_URL) + IMAGE_UPLOAD_SUFIX;

        imageUri = getConfiguration(EXTRA_ENTITY_URI);
        if (imageUri == null) {
            Log.w(LOG_TAG, "Missing required extra: image URI.");
            fail();
            return;
        } else {
            Log.d(LOG_TAG, EXTRA_ENTITY_URI + ": " + imageUri);
            // Coloca um thumbnail da imagem selecionada na tela.
            // FIXME A imagem geralmente é muito grande e esgota a memória do
            // aparelho;
            // é necessário carregar o thumbnail (ou gerar um, se não houver).
            // Uri uri = Uri.parse(sUri);
            // ImageView v = (ImageView) this.findViewById(R.id.image_preview);
            // v.setImageURI(uri);
            // v.setVisibility(View.VISIBLE);
        }

        bindService(new Intent(ILocationService.class.getName()),
                mLocationConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (pd != null) {
            pd.dismiss();
        }
    }

    public void confirmOnClick(View view) {
        String photoName = ((EditText) findViewById(R.id.image_name)).getText()
                .toString().trim();
        String description = ((EditText) findViewById(R.id.image_description))
                .getText().toString().trim();

        if ("".equals(photoName)) {
            Toast.makeText(this,
                    "Campo 'Título' está em branco e é obrigatório.",
                    Toast.LENGTH_LONG).show();
            return;
        }
        if ("".equals(description)) {
            Toast.makeText(this,
                    "Campo 'Descrição' está em branco e é obrigatório.",
                    Toast.LENGTH_LONG).show();
            return;
        }
        postData();
        cancela = false;
        pd = new ProgressDialog(this);
        pd.setTitle("Enviando imagem");
        pd.setIndeterminate(true);
        pd.setCancelable(true);
        pd.setMessage("Enviando foto selecionada ao servidor GW");
        pd.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                cancela = true;
                unbindService(connection);
                pd = null;
            }
        });
        pd.show();
    }

    private static final String INPUT_NAME_ID = "photoRegister.id";
    private static final String INPUT_NAME_NAME = "photoRegister.nome";
    private static final String INPUT_NAME_DESCRIPTION = "photoRegister.descricao";
    private static final String INPUT_NAME_CITY = "photoRegister.cidade";
    private static final String INPUT_NAME_STATE = "photoRegister.estado";
    private static final String INPUT_NAME_COUNTRY = "photoRegister.pais";
    private static final String INPUT_NAME_TAGS = "tagMgr.tags";
    private static final String INPUT_NAME_DATE = "photoRegister.data";
    private static final String INPUT_NAME_PHOTO = "foto";
    private static final String INPUT_NAME_LATITUDE = "geoReferenceMgr.latitude";
    private static final String INPUT_NAME_LONGITUDE = "geoReferenceMgr.longitude";

    // <form name="photoRegisterForm" method="post"
    // enctype="multipart/form-data"
    // action="/GW-Application-Arquigrafia/groupware-workbench/6/photo/registra">
    // <input type="hidden" name="photoRegister.id" value="" />
    // <input type="text" name="photoRegister.nome">
    // <textarea name="photoRegister.descricao"></textarea>
    // <input type="text" name="photoRegister.lugar">
    // <textarea name="tagMgr.tags"></textarea>
    // <input type="text" name="photoRegister.data" />
    // <input type="file" name="foto" />
    // </form>

    private void postData() {
        // Instancia a classe de conexão com o serviço de upload/download
        connection = new ConnectionServiceConnection(
                ImageUploader.class.getName()) {
            @Override
            public void onRequestFinished(String id, String filePath) {
                if (!cancela) {
                    try {
                        long photoId = PhotoParser.getId(new FileInputStream(
                                filePath));
                        ImageUploader.this.getIntent().putExtra(
                                EXTRA_ENTITY_ID, Long.toString(photoId));
                        ImageUploader.this.getIntent().putExtra(
                                EXTRA_ENTITY_CLASS, ENTITY_CLASS);
                        selectNextComponentAndFinish(null, false);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                unbindService(connection);
            }

            @Override
            public void onServiceConnected() {
                ReverseGeocodingTask rgt = new ReverseGeocodingTask();

                if (mGotGPSData) {
                    rgt.execute(mLatitude, mLongitude);
                } else {
                    rgt.execute();
                }

                // O envio é feito pelo ReverseGeocodingTask, já que o reverse
                // geocoding bloqueia a thread.
            }

            class ReverseGeocodingTask extends
                    AsyncTask<Double, Void, List<Address>> {

                @Override
                protected List<Address> doInBackground(Double... params) {
                    if (params == null || params.length != 2) {
                        return null;
                    }

                    List<Address> la = null;
                    Geocoder gc = new Geocoder(ImageUploader.this);
                    try {
                        la = gc.getFromLocation(params[0], params[1], 1);
                    } catch (IOException e) {
                        Log.e(LOG_TAG,
                                "Erro no reverse geocoding, para buscar o endereço da foto.",
                                e);
                    }
                    return la;
                }

                protected void onPostExecute(List<Address> result) {
                    String cidade = "";
                    String estado = "";
                    String pais = "";

                    if (result != null && result.size() > 0) {
                        Address a = result.get(0);
                        cidade = a.getLocality();
                        estado = a.getAdminArea();
                        pais = a.getCountryName();
                    }

                    String id = ""; // Intencionalmente vazio.
                    String photoName = ((EditText) findViewById(R.id.image_name))
                            .getText().toString().trim();
                    String description = ((EditText) findViewById(R.id.image_description))
                            .getText().toString().trim();
                    String tags = "mobile"; // Padronizei a tag 'mobile' para
                                            // fotos enviadas a partir do
                                            // celular
                    Calendar calendar = Calendar.getInstance();
                    String date = String.format("%2d/%2d/%4d",
                            calendar.get(Calendar.DAY_OF_MONTH),
                            calendar.get(Calendar.MONTH),
                            calendar.get(Calendar.YEAR));

                    Bundle stringParts = new Bundle();
                    stringParts.putString(INPUT_NAME_ID, id);
                    stringParts.putString(INPUT_NAME_NAME, photoName);
                    stringParts.putString(INPUT_NAME_DESCRIPTION, description);
                    stringParts.putString(INPUT_NAME_TAGS, tags);
                    stringParts.putString(INPUT_NAME_DATE, date);
                    stringParts.putString(INPUT_NAME_CITY, cidade);
                    stringParts.putString(INPUT_NAME_STATE, estado);
                    stringParts.putString(INPUT_NAME_COUNTRY, pais);

                    stringParts.putString(INPUT_NAME_LATITUDE,
                            Double.toString(mLatitude));
                    stringParts.putString(INPUT_NAME_LONGITUDE,
                            Double.toString(mLongitude));

                    Bundle fileParts = new Bundle();
                    fileParts.putString(INPUT_NAME_PHOTO, imageUri);

                    try {
                        registerPostRequest(mFormAction,
                                ConnectionServiceConnection.generateId(),
                                stringParts, fileParts);
                    } catch (Exception e) {
                        Log.w(LOG_TAG, e.getMessage(), e);
                    }
                    Log.d(LOG_TAG, "Requisição de upload enviada.");
                };
            }
        };

        bindService(new Intent(IConnectionService.class.getName()), connection,
                Context.BIND_AUTO_CREATE);
    }

    private static class PhotoParser {
        private static long photoId;

        private static long getId(InputStream is) {
            try {
                SAXParser parser = SAXParserFactory.newInstance()
                        .newSAXParser();
                PhotoHandler handler = new PhotoHandler();
                parser.parse(is, handler);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Erro no parsing da foto upada!", e);
            }

            return photoId;
        }

        private static class PhotoHandler extends DefaultHandler {
            private StringBuilder sb;

            @Override
            public void startElement(String uri, String localName,
                    String qName, org.xml.sax.Attributes attributes)
                    throws SAXException {
                sb = new StringBuilder();
            }

            @Override
            public void characters(char[] ch, int start, int length)
                    throws SAXException {
                sb.append(ch, start, length);
            }

            @Override
            public void endElement(String uri, String localName, String qName)
                    throws SAXException {
                super.endElement(uri, localName, qName);
                if ("id".equalsIgnoreCase(localName)) {
                    photoId = Long.parseLong(sb.toString());
                }
            }
        }
    }
}
